home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / pymodules / python2.6 / pyinotify.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-10-28  |  62KB  |  1,717 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''
  5. pyinotify
  6.  
  7. @author: Sebastien Martini
  8. @license: GPLv2+
  9. @contact: seb@dbzteam.org
  10. '''
  11.  
  12. class PyinotifyError(Exception):
  13.     '''Indicates exceptions raised by a Pyinotify class.'''
  14.     pass
  15.  
  16.  
  17. class UnsupportedPythonVersionError(PyinotifyError):
  18.     '''
  19.     Raised for unsupported Python version.
  20.     '''
  21.     
  22.     def __init__(self, version):
  23.         '''
  24.         @param version: Current Python version
  25.         @type version: string
  26.         '''
  27.         PyinotifyError.__init__(self, 'Python %s is unsupported, requires at least Python 2.4' % version)
  28.  
  29.  
  30.  
  31. class UnsupportedLibcVersionError(PyinotifyError):
  32.     '''
  33.     Raised for unsupported libc version.
  34.     '''
  35.     
  36.     def __init__(self, version):
  37.         '''
  38.         @param version: Current Libc version
  39.         @type version: string
  40.         '''
  41.         PyinotifyError.__init__(self, 'Libc %s is unsupported, requires at least Libc 2.4' % version)
  42.  
  43.  
  44. import sys
  45. if sys.version < '2.4':
  46.     raise UnsupportedPythonVersionError(sys.version)
  47. sys.version < '2.4'
  48. import threading
  49. import os
  50. import select
  51. import struct
  52. import fcntl
  53. import errno
  54. import termios
  55. import array
  56. import logging
  57. import atexit
  58. from collections import deque
  59. from datetime import datetime, timedelta
  60. import time
  61. import fnmatch
  62. import re
  63. import ctypes
  64. import ctypes.util as ctypes
  65. __author__ = 'seb@dbzteam.org (Sebastien Martini)'
  66. __version__ = '0.8.6'
  67. __metaclass__ = type
  68. LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
  69. LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
  70. LIBC_VERSION = LIBC.gnu_get_libc_version()
  71. if int(LIBC_VERSION.split('.')[0]) < 2 and int(LIBC_VERSION.split('.')[0]) == 2 and int(LIBC_VERSION.split('.')[1]) < 4:
  72.     raise UnsupportedLibcVersionError(LIBC_VERSION)
  73. int(LIBC_VERSION.split('.')[1]) < 4
  74. log = logging.getLogger('pyinotify')
  75. console_handler = logging.StreamHandler()
  76. console_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
  77. log.addHandler(console_handler)
  78. log.setLevel(20)
  79.  
  80. try:
  81.     if False:
  82.         import psyco
  83.         psyco.full()
  84. except ImportError:
  85.     pass
  86.  
  87.  
  88. class SysCtlINotify:
  89.     """
  90.     Access (read, write) inotify's variables through sysctl.
  91.  
  92.     Examples:
  93.       - Read variable: myvar = max_queued_events.value
  94.       - Update variable: max_queued_events.value = 42
  95.     """
  96.     inotify_attrs = {
  97.         'max_user_instances': 1,
  98.         'max_user_watches': 2,
  99.         'max_queued_events': 3 }
  100.     
  101.     def __init__(self, attrname):
  102.         sino = ctypes.c_int * 3
  103.         self._attrname = attrname
  104.         self._attr = sino(5, 20, SysCtlINotify.inotify_attrs[attrname])
  105.  
  106.     
  107.     def get_val(self):
  108.         '''
  109.         @return: stored value.
  110.         @rtype: int
  111.         '''
  112.         oldv = ctypes.c_int(0)
  113.         size = ctypes.c_int(ctypes.sizeof(oldv))
  114.         LIBC.sysctl(self._attr, 3, ctypes.c_voidp(ctypes.addressof(oldv)), ctypes.addressof(size), None, 0)
  115.         return oldv.value
  116.  
  117.     
  118.     def set_val(self, nval):
  119.         '''
  120.         @param nval: set to nval.
  121.         @type nval: int
  122.         '''
  123.         oldv = ctypes.c_int(0)
  124.         sizeo = ctypes.c_int(ctypes.sizeof(oldv))
  125.         newv = ctypes.c_int(nval)
  126.         sizen = ctypes.c_int(ctypes.sizeof(newv))
  127.         LIBC.sysctl(self._attr, 3, ctypes.c_voidp(ctypes.addressof(oldv)), ctypes.addressof(sizeo), ctypes.c_voidp(ctypes.addressof(newv)), ctypes.addressof(sizen))
  128.  
  129.     value = property(get_val, set_val)
  130.     
  131.     def __repr__(self):
  132.         return '<%s=%d>' % (self._attrname, self.get_val())
  133.  
  134.  
  135. for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
  136.     globals()[attrname] = SysCtlINotify(attrname)
  137.  
  138.  
  139. def iglob(pathname):
  140.     if not has_magic(pathname):
  141.         if hasattr(os.path, 'lexists'):
  142.             if os.path.lexists(pathname):
  143.                 yield pathname
  144.             
  145.         elif os.path.islink(pathname) or os.path.exists(pathname):
  146.             yield pathname
  147.         
  148.         return None
  149.     (dirname, basename) = os.path.split(pathname)
  150.     if not dirname:
  151.         return None
  152.     if has_magic(basename):
  153.         glob_in_dir = glob1
  154.     else:
  155.         glob_in_dir = glob0
  156.     for dirname in dirs:
  157.         for name in glob_in_dir(dirname, basename):
  158.             yield os.path.join(dirname, name)
  159.         
  160.     
  161.  
  162.  
  163. def glob1(dirname, pattern):
  164.     if not dirname:
  165.         dirname = os.curdir
  166.     
  167.     
  168.     try:
  169.         names = os.listdir(dirname)
  170.     except os.error:
  171.         return []
  172.  
  173.     return fnmatch.filter(names, pattern)
  174.  
  175.  
  176. def glob0(dirname, basename):
  177.     if basename == '' and os.path.isdir(dirname):
  178.         return [
  179.             basename]
  180.     None if hasattr(os.path, 'lexists') else os.path.isdir(dirname)
  181.     return []
  182.  
  183. magic_check = re.compile('[*?[]')
  184.  
  185. def has_magic(s):
  186.     return magic_check.search(s) is not None
  187.  
  188.  
  189. class EventsCodes:
  190.     """
  191.     Set of codes corresponding to each kind of events.
  192.     Some of these flags are used to communicate with inotify, whereas
  193.     the others are sent to userspace by inotify notifying some events.
  194.  
  195.     @cvar IN_ACCESS: File was accessed.
  196.     @type IN_ACCESS: int
  197.     @cvar IN_MODIFY: File was modified.
  198.     @type IN_MODIFY: int
  199.     @cvar IN_ATTRIB: Metadata changed.
  200.     @type IN_ATTRIB: int
  201.     @cvar IN_CLOSE_WRITE: Writtable file was closed.
  202.     @type IN_CLOSE_WRITE: int
  203.     @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
  204.     @type IN_CLOSE_NOWRITE: int
  205.     @cvar IN_OPEN: File was opened.
  206.     @type IN_OPEN: int
  207.     @cvar IN_MOVED_FROM: File was moved from X.
  208.     @type IN_MOVED_FROM: int
  209.     @cvar IN_MOVED_TO: File was moved to Y.
  210.     @type IN_MOVED_TO: int
  211.     @cvar IN_CREATE: Subfile was created.
  212.     @type IN_CREATE: int
  213.     @cvar IN_DELETE: Subfile was deleted.
  214.     @type IN_DELETE: int
  215.     @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
  216.     @type IN_DELETE_SELF: int
  217.     @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
  218.     @type IN_MOVE_SELF: int
  219.     @cvar IN_UNMOUNT: Backing fs was unmounted.
  220.     @type IN_UNMOUNT: int
  221.     @cvar IN_Q_OVERFLOW: Event queued overflowed.
  222.     @type IN_Q_OVERFLOW: int
  223.     @cvar IN_IGNORED: File was ignored.
  224.     @type IN_IGNORED: int
  225.     @cvar IN_ONLYDIR: only watch the path if it is a directory (new
  226.                       in kernel 2.6.15).
  227.     @type IN_ONLYDIR: int
  228.     @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
  229.                           IN_ONLYDIR we can make sure that we don't watch
  230.                           the target of symlinks.
  231.     @type IN_DONT_FOLLOW: int
  232.     @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
  233.                        in kernel 2.6.14).
  234.     @type IN_MASK_ADD: int
  235.     @cvar IN_ISDIR: Event occurred against dir.
  236.     @type IN_ISDIR: int
  237.     @cvar IN_ONESHOT: Only send event once.
  238.     @type IN_ONESHOT: int
  239.     @cvar ALL_EVENTS: Alias for considering all of the events.
  240.     @type ALL_EVENTS: int
  241.     """
  242.     FLAG_COLLECTIONS = {
  243.         'OP_FLAGS': {
  244.             'IN_ACCESS': 1,
  245.             'IN_MODIFY': 2,
  246.             'IN_ATTRIB': 4,
  247.             'IN_CLOSE_WRITE': 8,
  248.             'IN_CLOSE_NOWRITE': 16,
  249.             'IN_OPEN': 32,
  250.             'IN_MOVED_FROM': 64,
  251.             'IN_MOVED_TO': 128,
  252.             'IN_CREATE': 256,
  253.             'IN_DELETE': 512,
  254.             'IN_DELETE_SELF': 1024,
  255.             'IN_MOVE_SELF': 2048 },
  256.         'EVENT_FLAGS': {
  257.             'IN_UNMOUNT': 8192,
  258.             'IN_Q_OVERFLOW': 16384,
  259.             'IN_IGNORED': 32768 },
  260.         'SPECIAL_FLAGS': {
  261.             'IN_ONLYDIR': 16777216,
  262.             'IN_DONT_FOLLOW': 33554432,
  263.             'IN_MASK_ADD': 536870912,
  264.             'IN_ISDIR': 1073741824,
  265.             'IN_ONESHOT': 0x80000000L } }
  266.     
  267.     def maskname(mask):
  268.         '''
  269.         Return the event name associated to mask. IN_ISDIR is appended when
  270.         appropriate. Note: only one event is returned, because only one is
  271.         raised once at a time.
  272.  
  273.         @param mask: mask.
  274.         @type mask: int
  275.         @return: event name.
  276.         @rtype: str
  277.         '''
  278.         ms = mask
  279.         name = '%s'
  280.         if mask & IN_ISDIR:
  281.             ms = mask - IN_ISDIR
  282.             name = '%s|IN_ISDIR'
  283.         
  284.         return name % EventsCodes.ALL_VALUES[ms]
  285.  
  286.     maskname = staticmethod(maskname)
  287.  
  288. EventsCodes.ALL_FLAGS = { }
  289. EventsCodes.ALL_VALUES = { }
  290. for flagc, valc in EventsCodes.FLAG_COLLECTIONS.iteritems():
  291.     setattr(EventsCodes, flagc, valc)
  292.     EventsCodes.ALL_FLAGS.update(valc)
  293.     for name, val in valc.iteritems():
  294.         globals()[name] = val
  295.         EventsCodes.ALL_VALUES[val] = name
  296.     
  297.  
  298. ALL_EVENTS = reduce((lambda x, y: x | y), EventsCodes.OP_FLAGS.itervalues())
  299. EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
  300. EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
  301.  
  302. class _Event:
  303.     '''
  304.     Event structure, represent events raised by the system. This
  305.     is the base class and should be subclassed.
  306.  
  307.     '''
  308.     
  309.     def __init__(self, dict_):
  310.         '''
  311.         Attach attributes (contained in dict_) to self.
  312.         '''
  313.         for tpl in dict_.iteritems():
  314.             setattr(self, *tpl)
  315.         
  316.  
  317.     
  318.     def __repr__(self):
  319.         '''
  320.         @return: String representation.
  321.         @rtype: str
  322.         '''
  323.         s = ''
  324.         for attr, value in sorted(self.__dict__.items(), key = (lambda x: x[0])):
  325.             if attr.startswith('_'):
  326.                 continue
  327.             
  328.             if attr == 'mask':
  329.                 value = hex(getattr(self, attr))
  330.             elif isinstance(value, str) and not value:
  331.                 value = "''"
  332.             
  333.             s += ' %s%s%s' % (Color.FieldName(attr), Color.Punctuation('='), Color.FieldValue(value))
  334.         
  335.         s = '%s%s%s %s' % (Color.Punctuation('<'), Color.ClassName(self.__class__.__name__), s, Color.Punctuation('>'))
  336.         return s
  337.  
  338.  
  339.  
  340. class _RawEvent(_Event):
  341.     """
  342.     Raw event, it contains only the informations provided by the system.
  343.     It doesn't infer anything.
  344.     """
  345.     
  346.     def __init__(self, wd, mask, cookie, name):
  347.         '''
  348.         @param wd: Watch Descriptor.
  349.         @type wd: int
  350.         @param mask: Bitmask of events.
  351.         @type mask: int
  352.         @param cookie: Cookie.
  353.         @type cookie: int
  354.         @param name: Basename of the file or directory against which the
  355.                      event was raised, in case where the watched directory
  356.                      is the parent directory. None if the event was raised
  357.                      on the watched item itself.
  358.         @type name: string or None
  359.         '''
  360.         super(_RawEvent, self).__init__({
  361.             'wd': wd,
  362.             'mask': mask,
  363.             'cookie': cookie,
  364.             'name': name.rstrip('\x00') })
  365.         log.debug(repr(self))
  366.  
  367.  
  368.  
  369. class Event(_Event):
  370.     """
  371.     This class contains all the useful informations about the observed
  372.     event. However, the incorporation of each field is not guaranteed and
  373.     depends on the type of event. In effect, some fields are irrelevant
  374.     for some kind of event (for example 'cookie' is meaningless for
  375.     IN_CREATE whereas it is useful for IN_MOVE_TO).
  376.  
  377.     The possible fields are:
  378.       - wd (int): Watch Descriptor.
  379.       - mask (int): Mask.
  380.       - maskname (str): Readable event name.
  381.       - path (str): path of the file or directory being watched.
  382.       - name (str): Basename of the file or directory against which the
  383.               event was raised, in case where the watched directory
  384.               is the parent directory. None if the event was raised
  385.               on the watched item itself. This field is always provided
  386.               even if the string is ''.
  387.       - pathname (str): absolute path of: path + name
  388.       - cookie (int): Cookie.
  389.       - dir (bool): is the event raised against directory.
  390.  
  391.     """
  392.     
  393.     def __init__(self, raw):
  394.         '''
  395.         Concretely, this is the raw event plus inferred infos.
  396.         '''
  397.         _Event.__init__(self, raw)
  398.         self.maskname = EventsCodes.maskname(self.mask)
  399.         
  400.         try:
  401.             if self.name:
  402.                 self.pathname = os.path.abspath(os.path.join(self.path, self.name))
  403.             else:
  404.                 self.pathname = os.path.abspath(self.path)
  405.         except AttributeError:
  406.             pass
  407.  
  408.  
  409.  
  410.  
  411. class ProcessEventError(PyinotifyError):
  412.     '''
  413.     ProcessEventError Exception. Raised on ProcessEvent error.
  414.     '''
  415.     
  416.     def __init__(self, err):
  417.         '''
  418.         @param err: Exception error description.
  419.         @type err: string
  420.         '''
  421.         PyinotifyError.__init__(self, err)
  422.  
  423.  
  424.  
  425. class _ProcessEvent:
  426.     '''
  427.     Abstract processing event class.
  428.     '''
  429.     
  430.     def __call__(self, event):
  431.         '''
  432.         To behave like a functor the object must be callable.
  433.         This method is a dispatch method. Lookup order:
  434.           1. process_MASKNAME method
  435.           2. process_FAMILY_NAME method
  436.           3. otherwise call process_default
  437.  
  438.         @param event: Event to be processed.
  439.         @type event: Event object
  440.         @return: By convention when used from the ProcessEvent class:
  441.                  - Returning False or None (default value) means keep on
  442.                  executing next chained functors (see chain.py example).
  443.                  - Returning True instead means do not execute next
  444.                    processing functions.
  445.         @rtype: bool
  446.         @raise ProcessEventError: Event object undispatchable,
  447.                                   unknown event.
  448.         '''
  449.         stripped_mask = event.mask - (event.mask & IN_ISDIR)
  450.         maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
  451.         if maskname is None:
  452.             raise ProcessEventError('Unknown mask 0x%08x' % stripped_mask)
  453.         maskname is None
  454.         meth = getattr(self, 'process_' + maskname, None)
  455.         if meth is not None:
  456.             return meth(event)
  457.         meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
  458.         if meth is not None:
  459.             return meth(event)
  460.         return self.process_default(event)
  461.  
  462.     
  463.     def __repr__(self):
  464.         return '<%s>' % self.__class__.__name__
  465.  
  466.  
  467.  
  468. class _SysProcessEvent(_ProcessEvent):
  469.     '''
  470.     There is three kind of processing according to each event:
  471.  
  472.       1. special handling (deletion from internal container, bug, ...).
  473.       2. default treatment: which is applied to most of events.
  474.       4. IN_ISDIR is never sent alone, he is piggybacked with a standart
  475.          event, he is not processed as the others events, instead, its
  476.          value is captured and appropriately aggregated to dst event.
  477.     '''
  478.     
  479.     def __init__(self, wm, notifier):
  480.         '''
  481.  
  482.         @param wm: Watch Manager.
  483.         @type wm: WatchManager instance
  484.         @param notifier: notifier.
  485.         @type notifier: Instance of Notifier.
  486.         '''
  487.         self._watch_manager = wm
  488.         self._notifier = notifier
  489.         self._mv_cookie = { }
  490.         self._mv = { }
  491.  
  492.     
  493.     def cleanup(self):
  494.         '''
  495.         Cleanup (delete) old (>1mn) records contained in self._mv_cookie
  496.         and self._mv.
  497.         '''
  498.         date_cur_ = datetime.now()
  499.         for seq in [
  500.             self._mv_cookie,
  501.             self._mv]:
  502.             for k in seq.keys():
  503.                 if date_cur_ - seq[k][1] > timedelta(minutes = 1):
  504.                     log.debug('cleanup: deleting entry %s' % seq[k][0])
  505.                     del seq[k]
  506.                     continue
  507.             
  508.         
  509.  
  510.     
  511.     def process_IN_CREATE(self, raw_event):
  512.         """
  513.         If the event concerns a directory and the auto_add flag of the
  514.         targetted watch is set to True, a new watch is added on this
  515.         new directory, with the same attributes's values than those of
  516.         this watch.
  517.         """
  518.         if raw_event.mask & IN_ISDIR:
  519.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  520.             if watch_.auto_add:
  521.                 addw = self._watch_manager.add_watch
  522.                 newwd = addw(os.path.join(watch_.path, raw_event.name), watch_.mask, proc_fun = watch_.proc_fun, rec = False, auto_add = watch_.auto_add)
  523.                 base = os.path.join(watch_.path, raw_event.name)
  524.                 if newwd[base] > 0:
  525.                     for name in os.listdir(base):
  526.                         inner = os.path.join(base, name)
  527.                         if os.path.isdir(inner) and self._watch_manager.get_wd(inner) is None:
  528.                             rawevent = _RawEvent(newwd[base], IN_CREATE | IN_ISDIR, 0, name)
  529.                             self._notifier._eventq.append(rawevent)
  530.                             continue
  531.                     
  532.                 
  533.             
  534.         
  535.         return self.process_default(raw_event)
  536.  
  537.     
  538.     def process_IN_MOVED_FROM(self, raw_event):
  539.         '''
  540.         Map the cookie with the source path (+ date for cleaning).
  541.         '''
  542.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  543.         path_ = watch_.path
  544.         src_path = os.path.normpath(os.path.join(path_, raw_event.name))
  545.         self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
  546.         return self.process_default(raw_event, {
  547.             'cookie': raw_event.cookie })
  548.  
  549.     
  550.     def process_IN_MOVED_TO(self, raw_event):
  551.         '''
  552.         Map the source path with the destination path (+ date for
  553.         cleaning).
  554.         '''
  555.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  556.         path_ = watch_.path
  557.         dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
  558.         mv_ = self._mv_cookie.get(raw_event.cookie)
  559.         if mv_:
  560.             self._mv[mv_[0]] = (dst_path, datetime.now())
  561.         
  562.         return self.process_default(raw_event, {
  563.             'cookie': raw_event.cookie })
  564.  
  565.     
  566.     def process_IN_MOVE_SELF(self, raw_event):
  567.         """
  568.         STATUS: the following bug has been fixed in the recent kernels (fixme:
  569.         which version ?). Now it raises IN_DELETE_SELF instead.
  570.  
  571.         Old kernels are bugged, this event is raised when the watched item
  572.         was moved, so we must update its path, but under some circumstances it
  573.         can be impossible: if its parent directory and its destination
  574.         directory aren't watched. The kernel (see include/linux/fsnotify.h)
  575.         doesn't bring us enough informations like the destination path of
  576.         moved items.
  577.         """
  578.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  579.         src_path = watch_.path
  580.         mv_ = self._mv.get(src_path)
  581.         if mv_:
  582.             watch_.path = mv_[0]
  583.         else:
  584.             log.error('The path %s of this watch %s must not be trusted anymore' % (watch_.path, watch_))
  585.             if not watch_.path.endswith('-wrong-path'):
  586.                 watch_.path += '-wrong-path'
  587.             
  588.         return self.process_default(raw_event)
  589.  
  590.     
  591.     def process_IN_Q_OVERFLOW(self, raw_event):
  592.         '''
  593.         Only signal overflow, most of the common flags are irrelevant
  594.         for this event (path, wd, name).
  595.         '''
  596.         return Event({
  597.             'mask': raw_event.mask })
  598.  
  599.     
  600.     def process_IN_IGNORED(self, raw_event):
  601.         '''
  602.         The watch descriptor raised by this event is now ignored (forever),
  603.         it can be safely deleted from watch manager dictionary.
  604.         After this event we can be sure that neither the event queue
  605.         neither the system will raise an event associated to this wd.
  606.         '''
  607.         event_ = self.process_default(raw_event)
  608.         
  609.         try:
  610.             del self._watch_manager._wmd[raw_event.wd]
  611.         except KeyError:
  612.             err = None
  613.             log.error(err)
  614.  
  615.         return event_
  616.  
  617.     
  618.     def process_default(self, raw_event, to_append = { }):
  619.         '''
  620.         Common handling for the following events:
  621.  
  622.         IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
  623.         IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
  624.         '''
  625.         ret = None
  626.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  627.         if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
  628.             dir_ = watch_.dir
  629.         else:
  630.             dir_ = bool(raw_event.mask & IN_ISDIR)
  631.         dict_ = {
  632.             'wd': raw_event.wd,
  633.             'mask': raw_event.mask,
  634.             'path': watch_.path,
  635.             'name': raw_event.name,
  636.             'dir': dir_ }
  637.         dict_.update(to_append)
  638.         return Event(dict_)
  639.  
  640.  
  641.  
  642. class ProcessEvent(_ProcessEvent):
  643.     """
  644.     Process events objects, can be specialized via subclassing, thus its
  645.     behavior can be overriden:
  646.  
  647.     Note: you should not override __init__ in your subclass instead define
  648.     a my_init() method, this method will be called from the constructor of
  649.     this class with optional parameters.
  650.  
  651.       1. Provide methods, e.g. process_IN_DELETE for processing a given kind
  652.          of event (eg. IN_DELETE in this case).
  653.       2. Or/and provide methods for processing events by 'family', e.g.
  654.          process_IN_CLOSE method will process both IN_CLOSE_WRITE and
  655.          IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
  656.          process_IN_CLOSE_NOWRITE aren't defined).
  657.       3. Or/and override process_default for processing the remaining kind of
  658.          events.
  659.     """
  660.     pevent = None
  661.     
  662.     def __init__(self, pevent = None, **kargs):
  663.         '''
  664.         Enable chaining of ProcessEvent instances.
  665.  
  666.         @param pevent: optional callable object, will be called on event
  667.                        processing (before self).
  668.         @type pevent: callable
  669.         @param kargs: optional arguments delagated to template method my_init
  670.         @type kargs: dict
  671.         '''
  672.         self.pevent = pevent
  673.         self.my_init(**kargs)
  674.  
  675.     
  676.     def my_init(self, **kargs):
  677.         """
  678.         Override this method when subclassing if you want to achieve
  679.         custom initialization of your subclass' instance. You MUST pass
  680.         keyword arguments. This method does nothing by default.
  681.  
  682.         @param kargs: optional arguments delagated to template method my_init
  683.         @type kargs: dict
  684.         """
  685.         pass
  686.  
  687.     
  688.     def __call__(self, event):
  689.         stop_chaining = False
  690.         if self.pevent is not None:
  691.             stop_chaining = self.pevent(event)
  692.         
  693.         if not stop_chaining:
  694.             return _ProcessEvent.__call__(self, event)
  695.  
  696.     
  697.     def nested_pevent(self):
  698.         return self.pevent
  699.  
  700.     
  701.     def process_default(self, event):
  702.         '''
  703.         Default default processing event method. Print event
  704.         on standart output.
  705.  
  706.         @param event: Event to be processed.
  707.         @type event: Event instance
  708.         '''
  709.         print repr(event)
  710.  
  711.  
  712.  
  713. class ChainIfTrue(ProcessEvent):
  714.     '''
  715.     Makes conditional chaining depending on the result of the nested
  716.     processing instance.
  717.     '''
  718.     
  719.     def my_init(self, func):
  720.         self._func = func
  721.  
  722.     
  723.     def process_default(self, event):
  724.         return not self._func(event)
  725.  
  726.  
  727.  
  728. class Stats(ProcessEvent):
  729.     
  730.     def my_init(self):
  731.         self._start_time = time.time()
  732.         self._stats = { }
  733.         self._stats_lock = threading.Lock()
  734.  
  735.     
  736.     def process_default(self, event):
  737.         self._stats_lock.acquire()
  738.         
  739.         try:
  740.             events = event.maskname.split('|')
  741.             for event_name in events:
  742.                 count = self._stats.get(event_name, 0)
  743.                 self._stats[event_name] = count + 1
  744.         finally:
  745.             self._stats_lock.release()
  746.  
  747.  
  748.     
  749.     def _stats_copy(self):
  750.         self._stats_lock.acquire()
  751.         
  752.         try:
  753.             return self._stats.copy()
  754.         finally:
  755.             self._stats_lock.release()
  756.  
  757.  
  758.     
  759.     def __repr__(self):
  760.         stats = self._stats_copy()
  761.         t = int(time.time() - self._start_time)
  762.         if t < 60:
  763.             ts = str(t) + 'sec'
  764.         elif t <= t:
  765.             pass
  766.         elif t < 3600:
  767.             ts = '%dmn%dsec' % (t / 60, t % 60)
  768.         elif t <= t:
  769.             pass
  770.         elif t < 86400:
  771.             ts = '%dh%dmn' % (t / 3600, (t % 3600) / 60)
  772.         elif t >= 86400:
  773.             ts = '%dd%dh' % (t / 86400, (t % 86400) / 3600)
  774.         
  775.         stats['ElapsedTime'] = ts
  776.         l = []
  777.         for ev, value in sorted(stats.items(), key = (lambda x: x[0])):
  778.             l.append(' %s=%s' % (Color.FieldName(ev), Color.FieldValue(value)))
  779.         
  780.         s = '<%s%s >' % (Color.ClassName(self.__class__.__name__), ''.join(l))
  781.         return s
  782.  
  783.     
  784.     def dump(self, filename):
  785.         fo = file(filename, 'wb')
  786.         
  787.         try:
  788.             fo.write(str(self))
  789.         finally:
  790.             fo.close()
  791.  
  792.  
  793.     
  794.     def __str__(self, scale = 45):
  795.         stats = self._stats_copy()
  796.         if not stats:
  797.             return ''
  798.         m = max(stats.values())
  799.         if not int(round(float(m) / scale)):
  800.             pass
  801.         unity = 1
  802.         fmt = '%%-26s%%-%ds%%s' % (len(Color.FieldValue('@' * scale)) + 1)
  803.         
  804.         def func(x):
  805.             return fmt % (Color.FieldName(x[0]), Color.FieldValue('@' * (x[1] / unity)), Color.Simple('%d' % x[1], 'yellow'))
  806.  
  807.         s = '\n'.join(map(func, sorted(stats.items(), key = (lambda x: x[0]))))
  808.         return s
  809.  
  810.  
  811.  
  812. class NotifierError(PyinotifyError):
  813.     '''
  814.     Notifier Exception. Raised on Notifier error.
  815.  
  816.     '''
  817.     
  818.     def __init__(self, err):
  819.         """
  820.         @param err: Exception string's description.
  821.         @type err: string
  822.         """
  823.         PyinotifyError.__init__(self, err)
  824.  
  825.  
  826.  
  827. class Notifier:
  828.     '''
  829.     Read notifications, process events.
  830.  
  831.     '''
  832.     
  833.     def __init__(self, watch_manager, default_proc_fun = ProcessEvent(), read_freq = 0, treshold = 0, timeout = None):
  834.         '''
  835.         Initialization. read_freq, treshold and timeout parameters are used
  836.         when looping.
  837.  
  838.         @param watch_manager: Watch Manager.
  839.         @type watch_manager: WatchManager instance
  840.         @param default_proc_fun: Default processing method.
  841.         @type default_proc_fun: instance of ProcessEvent
  842.         @param read_freq: if read_freq == 0, events are read asap,
  843.                           if read_freq is > 0, this thread sleeps
  844.                           max(0, read_freq - timeout) seconds. But if
  845.                           timeout is None it can be different because
  846.                           poll is blocking waiting for something to read.
  847.         @type read_freq: int
  848.         @param treshold: File descriptor will be read only if its size to
  849.                          read is >= treshold. If != 0, you likely want to
  850.                          use it in combination with read_freq because
  851.                          without that you keep looping without really reading
  852.                          anything and that until the amount to read
  853.                          is >= treshold. At least with read_freq you may sleep.
  854.         @type treshold: int
  855.         @param timeout:
  856.             http://docs.python.org/lib/poll-objects.html#poll-objects
  857.         @type timeout: int
  858.         '''
  859.         self._watch_manager = watch_manager
  860.         self._fd = self._watch_manager._fd
  861.         self._pollobj = select.poll()
  862.         self._pollobj.register(self._fd, select.POLLIN)
  863.         self._pipe = (-1, -1)
  864.         self._eventq = deque()
  865.         self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
  866.         self._default_proc_fun = default_proc_fun
  867.         self._read_freq = read_freq
  868.         self._treshold = treshold
  869.         self._timeout = timeout
  870.  
  871.     
  872.     def proc_fun(self):
  873.         return self._default_proc_fun
  874.  
  875.     
  876.     def check_events(self):
  877.         '''
  878.         Check for new events available to read, blocks up to timeout
  879.         milliseconds.
  880.  
  881.         @return: New events to read.
  882.         @rtype: bool
  883.         '''
  884.         while True:
  885.             
  886.             try:
  887.                 ret = self._pollobj.poll(self._timeout)
  888.             except select.error:
  889.                 err = None
  890.                 if err[0] == errno.EINTR:
  891.                     continue
  892.                 else:
  893.                     raise 
  894.                 err[0] == errno.EINTR
  895.  
  896.             break
  897.         if not ret or self._pipe[0] == ret[0][0]:
  898.             return False
  899.         return ret[0][1] & select.POLLIN
  900.  
  901.     
  902.     def read_events(self):
  903.         '''
  904.         Read events from device, build _RawEvents, and enqueue them.
  905.         '''
  906.         buf_ = array.array('i', [
  907.             0])
  908.         if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
  909.             return None
  910.         queue_size = buf_[0]
  911.         if queue_size < self._treshold:
  912.             log.debug('(fd: %d) %d bytes available to read but treshold is fixed to %d bytes' % (self._fd, queue_size, self._treshold))
  913.             return None
  914.         
  915.         try:
  916.             r = os.read(self._fd, queue_size)
  917.         except Exception:
  918.             queue_size < self._treshold
  919.             msg = queue_size < self._treshold
  920.             fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1
  921.             raise NotifierError(msg)
  922.         except:
  923.             queue_size < self._treshold
  924.  
  925.         log.debug('event queue size: %d' % queue_size)
  926.         rsum = 0
  927.         while rsum < queue_size:
  928.             s_size = 16
  929.             s_ = struct.unpack('iIII', r[rsum:rsum + s_size])
  930.             fname_len = s_[3]
  931.             s_ = s_[:-1]
  932.             s_ += struct.unpack('%ds' % fname_len, r[rsum + s_size:rsum + s_size + fname_len])
  933.             self._eventq.append(_RawEvent(*s_))
  934.             rsum += s_size + fname_len
  935.             continue
  936.             queue_size < self._treshold
  937.  
  938.     
  939.     def process_events(self):
  940.         '''
  941.         Routine for processing events from queue by calling their
  942.         associated proccessing function (instance of ProcessEvent).
  943.         It also do internal processings, to keep the system updated.
  944.         '''
  945.         while self._eventq:
  946.             raw_event = self._eventq.popleft()
  947.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  948.             revent = self._sys_proc_fun(raw_event)
  949.             if watch_ and watch_.proc_fun:
  950.                 watch_.proc_fun(revent)
  951.                 continue
  952.             self._default_proc_fun(revent)
  953.         self._sys_proc_fun.cleanup()
  954.  
  955.     
  956.     def __daemonize(self, pid_file = None, force_kill = False, stdin = os.devnull, stdout = os.devnull, stderr = os.devnull):
  957.         '''
  958.         pid_file: file to which pid will be written.
  959.         force_kill: if True kill the process associated to pid_file.
  960.         stdin, stdout, stderr: files associated to common streams.
  961.         '''
  962.         if pid_file is None:
  963.             dirname = '/var/run/'
  964.             if not sys.argv[0]:
  965.                 pass
  966.             basename = 'pyinotify'
  967.             pid_file = os.path.join(dirname, basename + '.pid')
  968.         
  969.         
  970.         def fork_daemon():
  971.             pid = os.fork()
  972.             if pid == 0:
  973.                 os.setsid()
  974.                 pid = os.fork()
  975.                 if pid == 0:
  976.                     os.chdir('/')
  977.                     os.umask(0)
  978.                 else:
  979.                     os._exit(0)
  980.             else:
  981.                 os._exit(0)
  982.             fd_inp = os.open(stdin, os.O_RDONLY)
  983.             os.dup2(fd_inp, 0)
  984.             fd_out = os.open(stdout, os.O_WRONLY | os.O_CREAT)
  985.             os.dup2(fd_out, 1)
  986.             fd_err = os.open(stderr, os.O_WRONLY | os.O_CREAT)
  987.             os.dup2(fd_err, 2)
  988.  
  989.         fork_daemon()
  990.         fo = file(pid_file, 'wb')
  991.         
  992.         try:
  993.             fo.write(str(os.getpid()) + '\n')
  994.         finally:
  995.             fo.close()
  996.  
  997.         (atexit.register,)((lambda : os.unlink(pid_file)))
  998.  
  999.     
  1000.     def _sleep(self, ref_time):
  1001.         if self._read_freq > 0:
  1002.             cur_time = time.time()
  1003.             sleep_amount = self._read_freq - cur_time - ref_time
  1004.             if sleep_amount > 0:
  1005.                 log.debug('Now sleeping %d seconds' % sleep_amount)
  1006.                 time.sleep(sleep_amount)
  1007.             
  1008.         
  1009.  
  1010.     
  1011.     def loop(self, callback = None, daemonize = False, **args):
  1012.         '''
  1013.         Events are read only once time every min(read_freq, timeout)
  1014.         seconds at best and only if the size to read is >= treshold.
  1015.  
  1016.         @param callback: Functor called after each event processing. Expects
  1017.                          to receive notifier object (self) as first parameter.
  1018.         @type callback: callable
  1019.         @param daemonize: This thread is daemonized if set to True.
  1020.         @type daemonize: boolean
  1021.         '''
  1022.         if daemonize:
  1023.             self._Notifier__daemonize(**args)
  1024.         
  1025.         while None:
  1026.             
  1027.             try:
  1028.                 self.process_events()
  1029.                 if callback is not None:
  1030.                     callback(self)
  1031.                 
  1032.                 ref_time = time.time()
  1033.                 if self.check_events():
  1034.                     self._sleep(ref_time)
  1035.                     self.read_events()
  1036.             continue
  1037.             except KeyboardInterrupt:
  1038.                 log.debug('Pyinotify stops monitoring.')
  1039.                 self.stop()
  1040.                 break
  1041.                 continue
  1042.             
  1043.  
  1044.             return None
  1045.  
  1046.     
  1047.     def stop(self):
  1048.         """
  1049.         Close the inotify's instance (close its file descriptor).
  1050.         It destroys all existing watches, pending events,...
  1051.         """
  1052.         self._pollobj.unregister(self._fd)
  1053.         os.close(self._fd)
  1054.  
  1055.  
  1056.  
  1057. class ThreadedNotifier(threading.Thread, Notifier):
  1058.     '''
  1059.     This notifier inherits from threading.Thread for instantiating a separate
  1060.     thread, and also inherits from Notifier, because it is a threaded notifier.
  1061.  
  1062.     Note that everything possible with this class is also possible through
  1063.     Notifier. Moreover Notifier is _better_ under many aspects: not threaded,
  1064.     can be easily daemonized.
  1065.     '''
  1066.     
  1067.     def __init__(self, watch_manager, default_proc_fun = ProcessEvent(), read_freq = 0, treshold = 0, timeout = None):
  1068.         '''
  1069.         Initialization, initialize base classes. read_freq, treshold and
  1070.         timeout parameters are used when looping.
  1071.  
  1072.         @param watch_manager: Watch Manager.
  1073.         @type watch_manager: WatchManager instance
  1074.         @param default_proc_fun: Default processing method.
  1075.         @type default_proc_fun: instance of ProcessEvent
  1076.         @param read_freq: if read_freq == 0, events are read asap,
  1077.                           if read_freq is > 0, this thread sleeps
  1078.                           max(0, read_freq - timeout) seconds.
  1079.         @type read_freq: int
  1080.         @param treshold: File descriptor will be read only if its size to
  1081.                          read is >= treshold. If != 0, you likely want to
  1082.                          use it in combination with read_freq because
  1083.                          without that you keep looping without really reading
  1084.                          anything and that until the amount to read
  1085.                          is >= treshold. At least with read_freq you may sleep.
  1086.         @type treshold: int
  1087.         @param timeout:
  1088.            see http://docs.python.org/lib/poll-objects.html#poll-objects
  1089.            Read the corresponding comment in the source code before changing
  1090.            it.
  1091.         @type timeout: int
  1092.         '''
  1093.         threading.Thread.__init__(self)
  1094.         self._stop_event = threading.Event()
  1095.         Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, treshold, timeout)
  1096.         self._pipe = os.pipe()
  1097.         self._pollobj.register(self._pipe[0], select.POLLIN)
  1098.  
  1099.     
  1100.     def stop(self):
  1101.         """
  1102.         Stop the notifier's loop. Stop notification. Join the thread.
  1103.         """
  1104.         self._stop_event.set()
  1105.         os.write(self._pipe[1], 'stop')
  1106.         threading.Thread.join(self)
  1107.         Notifier.stop(self)
  1108.         self._pollobj.unregister(self._pipe[0])
  1109.         os.close(self._pipe[0])
  1110.         os.close(self._pipe[1])
  1111.  
  1112.     
  1113.     def loop(self):
  1114.         """
  1115.         Thread's main loop. Don't meant to be called by user directly.
  1116.         Call start() instead.
  1117.  
  1118.         Events are read only once time every min(read_freq, timeout)
  1119.         seconds at best and only if the size of events to read is >= treshold.
  1120.         """
  1121.         while not self._stop_event.isSet():
  1122.             self.process_events()
  1123.             ref_time = time.time()
  1124.             if self.check_events():
  1125.                 self._sleep(ref_time)
  1126.                 self.read_events()
  1127.                 continue
  1128.  
  1129.     
  1130.     def run(self):
  1131.         """
  1132.         Start the thread's loop: read and process events until the method
  1133.         stop() is called.
  1134.         Never call this method directly, instead call the start() method
  1135.         inherited from threading.Thread, which then will call run().
  1136.         """
  1137.         self.loop()
  1138.  
  1139.  
  1140.  
  1141. class Watch:
  1142.     '''
  1143.     Represent a watch, i.e. a file or directory being watched.
  1144.  
  1145.     '''
  1146.     
  1147.     def __init__(self, **keys):
  1148.         '''
  1149.         Initializations.
  1150.  
  1151.         @param wd: Watch descriptor.
  1152.         @type wd: int
  1153.         @param path: Path of the file or directory being watched.
  1154.         @type path: str
  1155.         @param mask: Mask.
  1156.         @type mask: int
  1157.         @param proc_fun: Processing callable object.
  1158.         @type proc_fun:
  1159.         @param auto_add: Automatically add watches on new directories.
  1160.         @type auto_add: bool
  1161.         '''
  1162.         for k, v in keys.iteritems():
  1163.             setattr(self, k, v)
  1164.         
  1165.         self.dir = os.path.isdir(self.path)
  1166.  
  1167.     
  1168.     def __repr__(self):
  1169.         '''
  1170.         @return: String representation.
  1171.         @rtype: str
  1172.         '''
  1173.         s = [](_[1])
  1174.         s = '%s%s %s %s' % (Color.Punctuation('<'), Color.ClassName(self.__class__.__name__), s, Color.Punctuation('>'))
  1175.         return s
  1176.  
  1177.  
  1178.  
  1179. class ExcludeFilter:
  1180.     '''
  1181.     ExcludeFilter is an exclusion filter.
  1182.     '''
  1183.     
  1184.     def __init__(self, arg_lst):
  1185.         """
  1186.         @param arg_lst: is either a list or dict of patterns:
  1187.                         [pattern1, ..., patternn]
  1188.                         {'filename1': (list1, listn), ...} where list1 is
  1189.                         a list of patterns
  1190.         @type arg_lst: list or dict
  1191.         """
  1192.         if isinstance(arg_lst, dict):
  1193.             lst = self._load_patterns(arg_lst)
  1194.         elif isinstance(arg_lst, list):
  1195.             lst = arg_lst
  1196.         else:
  1197.             raise TypeError
  1198.         self._lregex = isinstance(arg_lst, dict)
  1199.         for regex in lst:
  1200.             self._lregex.append(re.compile(regex, re.UNICODE))
  1201.         
  1202.  
  1203.     
  1204.     def _load_patterns(self, dct):
  1205.         lst = []
  1206.         for path, varnames in dct.iteritems():
  1207.             loc = { }
  1208.             execfile(path, { }, loc)
  1209.             for varname in varnames:
  1210.                 lst.extend(loc.get(varname, []))
  1211.             
  1212.         
  1213.         return lst
  1214.  
  1215.     
  1216.     def _match(self, regex, path):
  1217.         return regex.match(path) is not None
  1218.  
  1219.     
  1220.     def __call__(self, path):
  1221.         '''
  1222.         @param path: path to match against regexps.
  1223.         @type path: str
  1224.         @return: return True is path has been matched and should
  1225.                  be excluded, False otherwise.
  1226.         @rtype: bool
  1227.         '''
  1228.         for regex in self._lregex:
  1229.             if self._match(regex, path):
  1230.                 return True
  1231.         
  1232.         return False
  1233.  
  1234.  
  1235.  
  1236. class WatchManagerError(Exception):
  1237.     '''
  1238.     WatchManager Exception. Raised on error encountered on watches
  1239.     operations.
  1240.  
  1241.     '''
  1242.     
  1243.     def __init__(self, msg, wmd):
  1244.         """
  1245.         @param msg: Exception string's description.
  1246.         @type msg: string
  1247.         @param wmd: Results of previous operations made by the same function
  1248.                     on previous wd or paths. It also contains the item which
  1249.                     raised this exception.
  1250.         @type wmd: dict
  1251.         """
  1252.         self.wmd = wmd
  1253.         Exception.__init__(self, msg)
  1254.  
  1255.  
  1256.  
  1257. class WatchManager:
  1258.     '''
  1259.     Provide operations for watching files and directories. Integrated
  1260.     dictionary is used to reference watched items.
  1261.     '''
  1262.     
  1263.     def __init__(self, exclude_filter = (lambda path: False)):
  1264.         '''
  1265.         Initialization: init inotify, init watch manager dictionary.
  1266.         Raise OSError if initialization fails.
  1267.  
  1268.         @param exclude_filter: boolean function, returns True if current
  1269.                                path must be excluded from being watched.
  1270.                                Convenient for providing a common exclusion
  1271.                                filter for every call to add_watch.
  1272.         @type exclude_filter: bool
  1273.         '''
  1274.         self._exclude_filter = exclude_filter
  1275.         self._wmd = { }
  1276.         self._fd = LIBC.inotify_init()
  1277.         if self._fd < 0:
  1278.             raise OSError()
  1279.         self._fd < 0
  1280.  
  1281.     
  1282.     def __add_watch(self, path, mask, proc_fun, auto_add):
  1283.         '''
  1284.         Add a watch on path, build a Watch object and insert it in the
  1285.         watch manager dictionary. Return the wd value.
  1286.         '''
  1287.         wd_ = LIBC.inotify_add_watch(self._fd, ctypes.create_string_buffer(path), mask)
  1288.         if wd_ < 0:
  1289.             return wd_
  1290.         watch_ = Watch(wd = wd_, path = os.path.normpath(path), mask = mask, proc_fun = proc_fun, auto_add = auto_add)
  1291.         self._wmd[wd_] = watch_
  1292.         log.debug('New %s' % watch_)
  1293.         return wd_
  1294.  
  1295.     
  1296.     def __glob(self, path, do_glob):
  1297.         if do_glob:
  1298.             return iglob(path)
  1299.         return [
  1300.             path]
  1301.  
  1302.     
  1303.     def add_watch(self, path, mask, proc_fun = None, rec = False, auto_add = False, do_glob = False, quiet = True, exclude_filter = None):
  1304.         """
  1305.         Add watch(s) on given path(s) with the specified mask and
  1306.         optionnally with a processing function and recursive flag.
  1307.  
  1308.         @param path: Path to watch, the path can either be a file or a
  1309.                      directory. Also accepts a sequence (list) of paths.
  1310.         @type path: string or list of string
  1311.         @param mask: Bitmask of events.
  1312.         @type mask: int
  1313.         @param proc_fun: Processing object.
  1314.         @type proc_fun: function or ProcessEvent instance or instance of
  1315.                         one of its subclasses or callable object.
  1316.         @param rec: Recursively add watches from path on all its
  1317.                     subdirectories, set to False by default (doesn't
  1318.                     follows symlinks).
  1319.         @type rec: bool
  1320.         @param auto_add: Automatically add watches on newly created
  1321.                          directories in the watch's path.
  1322.         @type auto_add: bool
  1323.         @param do_glob: Do globbing on pathname.
  1324.         @type do_glob: bool
  1325.         @param quiet: if True raise an WatchManagerError exception on
  1326.                       error. See example not_quiet.py
  1327.         @type quiet: bool
  1328.         @param exclude_filter: boolean function, returns True if current
  1329.                                path must be excluded from being watched.
  1330.                                Has precedence on exclude_filter defined
  1331.                                into __init__.
  1332.         @type exclude_filter: bool
  1333.         @return: dict of paths associated to watch descriptors. A wd value
  1334.                  is positive if the watch has been sucessfully added,
  1335.                  otherwise the value is negative. If the path is invalid
  1336.                  it will be not included into this dict.
  1337.         @rtype: dict of {str: int}
  1338.         """
  1339.         ret_ = { }
  1340.         if exclude_filter is None:
  1341.             exclude_filter = self._exclude_filter
  1342.         
  1343.         for npath in self._WatchManager__format_param(path):
  1344.             for apath in self._WatchManager__glob(npath, do_glob):
  1345.                 for rpath in self._WatchManager__walk_rec(apath, rec):
  1346.                     if not exclude_filter(rpath):
  1347.                         wd = ret_[rpath] = self._WatchManager__add_watch(rpath, mask, proc_fun, auto_add)
  1348.                         if wd < 0:
  1349.                             err = 'add_watch: cannot watch %s (WD=%d)'
  1350.                             err = err % (rpath, wd)
  1351.                             if quiet:
  1352.                                 log.error(err)
  1353.                             else:
  1354.                                 raise WatchManagerError(err, ret_)
  1355.                         quiet
  1356.                         continue
  1357.                     ret_[rpath] = -2
  1358.                 
  1359.             
  1360.         
  1361.         return ret_
  1362.  
  1363.     
  1364.     def __get_sub_rec(self, lpath):
  1365.         """
  1366.         Get every wd from self._wmd if its path is under the path of
  1367.         one (at least) of those in lpath. Doesn't follow symlinks.
  1368.  
  1369.         @param lpath: list of watch descriptor
  1370.         @type lpath: list of int
  1371.         @return: list of watch descriptor
  1372.         @rtype: list of int
  1373.         """
  1374.         for d in lpath:
  1375.             root = self.get_path(d)
  1376.             if root:
  1377.                 yield d
  1378.             
  1379.             if not os.path.isdir(root):
  1380.                 continue
  1381.             
  1382.             root = os.path.normpath(root)
  1383.             lend = len(root)
  1384.             for iwd in self._wmd.items():
  1385.                 cur = iwd[1].path
  1386.                 pref = os.path.commonprefix([
  1387.                     root,
  1388.                     cur])
  1389.                 if (root == os.sep or len(pref) == lend) and len(cur) > lend and cur[lend] == os.sep:
  1390.                     yield iwd[1].wd
  1391.                     continue
  1392.             
  1393.         
  1394.  
  1395.     
  1396.     def update_watch(self, wd, mask = None, proc_fun = None, rec = False, auto_add = False, quiet = True):
  1397.         """
  1398.         Update existing watch(s). Both the mask and the processing
  1399.         object can be modified.
  1400.  
  1401.         @param wd: Watch Descriptor to update. Also accepts a list of
  1402.                      watch descriptors.
  1403.         @type wd: int or list of int
  1404.         @param mask: Optional new bitmask of events.
  1405.         @type mask: int
  1406.         @param proc_fun: Optional new processing function.
  1407.         @type proc_fun: function or ProcessEvent instance or instance of
  1408.                         one of its subclasses or callable object.
  1409.         @param rec: Recursively update watches on every already watched
  1410.                     subdirectories and subfiles.
  1411.         @type rec: bool
  1412.         @param auto_add: Automatically add watches on newly created
  1413.                          directories in the watch's path.
  1414.         @type auto_add: bool
  1415.         @param quiet: if True raise an WatchManagerError exception on
  1416.                       error. See example not_quiet.py
  1417.         @type quiet: bool
  1418.         @return: dict of watch descriptors associated to booleans values.
  1419.                  True if the corresponding wd has been successfully
  1420.                  updated, False otherwise.
  1421.         @rtype: dict of int: bool
  1422.         """
  1423.         lwd = self._WatchManager__format_param(wd)
  1424.         if rec:
  1425.             lwd = self._WatchManager__get_sub_rec(lwd)
  1426.         
  1427.         ret_ = { }
  1428.         for awd in lwd:
  1429.             apath = self.get_path(awd)
  1430.             if not apath or awd < 0:
  1431.                 err = 'update_watch: invalid WD=%d' % awd
  1432.                 if quiet:
  1433.                     log.error(err)
  1434.                     continue
  1435.                 
  1436.                 raise WatchManagerError(err, ret_)
  1437.             awd < 0
  1438.             if mask:
  1439.                 addw = LIBC.inotify_add_watch
  1440.                 wd_ = addw(self._fd, ctypes.create_string_buffer(apath), mask)
  1441.                 if wd_ < 0:
  1442.                     ret_[awd] = False
  1443.                     err = 'update_watch: cannot update WD=%d (%s)' % (wd_, apath)
  1444.                     if quiet:
  1445.                         log.error(err)
  1446.                         continue
  1447.                     
  1448.                     raise WatchManagerError(err, ret_)
  1449.                 wd_ < 0
  1450.                 if not awd == wd_:
  1451.                     raise AssertionError
  1452.             
  1453.             if proc_fun or auto_add:
  1454.                 watch_ = self._wmd[awd]
  1455.             
  1456.             if proc_fun:
  1457.                 watch_.proc_fun = proc_fun
  1458.             
  1459.             if auto_add:
  1460.                 watch_.proc_fun = auto_add
  1461.             
  1462.             ret_[awd] = True
  1463.             log.debug('Updated watch - %s' % self._wmd[awd])
  1464.         
  1465.         return ret_
  1466.  
  1467.     
  1468.     def __format_param(self, param):
  1469.         '''
  1470.         @param param: Parameter.
  1471.         @type param: string or int
  1472.         @return: wrap param.
  1473.         @rtype: list of type(param)
  1474.         '''
  1475.         if isinstance(param, list):
  1476.             for p_ in param:
  1477.                 yield p_
  1478.             
  1479.         else:
  1480.             yield param
  1481.  
  1482.     
  1483.     def get_wd(self, path):
  1484.         '''
  1485.         Returns the watch descriptor associated to path. This method
  1486.         has an prohibitive cost, always prefer to keep the WD.
  1487.         If path is unknown None is returned.
  1488.  
  1489.         @param path: path.
  1490.         @type path: str
  1491.         @return: WD or None.
  1492.         @rtype: int or None
  1493.         '''
  1494.         path = os.path.normpath(path)
  1495.         for iwd in self._wmd.iteritems():
  1496.             if iwd[1].path == path:
  1497.                 return iwd[0]
  1498.         
  1499.         log.debug('get_wd: unknown path %s' % path)
  1500.  
  1501.     
  1502.     def get_path(self, wd):
  1503.         '''
  1504.         Returns the path associated to WD, if WD is unknown
  1505.         None is returned.
  1506.  
  1507.         @param wd: watch descriptor.
  1508.         @type wd: int
  1509.         @return: path or None.
  1510.         @rtype: string or None
  1511.         '''
  1512.         watch_ = self._wmd.get(wd)
  1513.         if watch_:
  1514.             return watch_.path
  1515.         log.debug('get_path: unknown WD %d' % wd)
  1516.  
  1517.     
  1518.     def __walk_rec(self, top, rec):
  1519.         """
  1520.         Yields each subdirectories of top, doesn't follow symlinks.
  1521.         If rec is false, only yield top.
  1522.  
  1523.         @param top: root directory.
  1524.         @type top: string
  1525.         @param rec: recursive flag.
  1526.         @type rec: bool
  1527.         @return: path of one subdirectory.
  1528.         @rtype: string
  1529.         """
  1530.         if not rec and os.path.islink(top) or not os.path.isdir(top):
  1531.             yield top
  1532.         else:
  1533.             for root, dirs, files in os.walk(top):
  1534.                 yield root
  1535.             
  1536.  
  1537.     
  1538.     def rm_watch(self, wd, rec = False, quiet = True):
  1539.         '''
  1540.         Removes watch(s).
  1541.  
  1542.         @param wd: Watch Descriptor of the file or directory to unwatch.
  1543.                    Also accepts a list of WDs.
  1544.         @type wd: int or list of int.
  1545.         @param rec: Recursively removes watches on every already watched
  1546.                     subdirectories and subfiles.
  1547.         @type rec: bool
  1548.         @param quiet: if True raise an WatchManagerError exception on
  1549.                       error. See example not_quiet.py
  1550.         @type quiet: bool
  1551.         @return: dict of watch descriptors associated to booleans values.
  1552.                  True if the corresponding wd has been successfully
  1553.                  removed, False otherwise.
  1554.         @rtype: dict of int: bool
  1555.         '''
  1556.         lwd = self._WatchManager__format_param(wd)
  1557.         if rec:
  1558.             lwd = self._WatchManager__get_sub_rec(lwd)
  1559.         
  1560.         ret_ = { }
  1561.         for awd in lwd:
  1562.             wd_ = LIBC.inotify_rm_watch(self._fd, awd)
  1563.             if wd_ < 0:
  1564.                 ret_[awd] = False
  1565.                 err = 'rm_watch: cannot remove WD=%d' % awd
  1566.                 if quiet:
  1567.                     log.error(err)
  1568.                     continue
  1569.                 
  1570.                 raise WatchManagerError(err, ret_)
  1571.             wd_ < 0
  1572.             ret_[awd] = True
  1573.             log.debug('watch WD=%d (%s) removed' % (awd, self.get_path(awd)))
  1574.         
  1575.         return ret_
  1576.  
  1577.     
  1578.     def watch_transient_file(self, filename, mask, proc_class):
  1579.         """
  1580.         Watch a transient file, which will be created and deleted frequently
  1581.         over time (e.g. pid file).
  1582.  
  1583.         @attention: Under the call to this function it will be impossible
  1584.         to correctly watch the events triggered into the same
  1585.         base directory than the directory where is located this watched
  1586.         transient file. For instance it would actually be wrong to make these
  1587.         two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
  1588.         and wm.add_watch('/var/run/', ...)
  1589.  
  1590.         @param filename: Filename.
  1591.         @type filename: string
  1592.         @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
  1593.         @type mask: int
  1594.         @param proc_class: ProcessEvent (or of one of its subclass), beware of
  1595.                            accepting a ProcessEvent's instance as argument into
  1596.                            __init__, see transient_file.py example for more
  1597.                            details.
  1598.         @type proc_class: ProcessEvent's instance or of one of its subclasses.
  1599.         @return: See add_watch().
  1600.         @rtype: See add_watch().
  1601.         """
  1602.         dirname = os.path.dirname(filename)
  1603.         if dirname == '':
  1604.             return { }
  1605.         basename = os.path.basename(filename)
  1606.         mask |= IN_CREATE | IN_DELETE
  1607.         
  1608.         def cmp_name(event):
  1609.             return basename == event.name
  1610.  
  1611.         return self.add_watch(dirname, mask, proc_fun = proc_class(ChainIfTrue(func = cmp_name)), rec = False, auto_add = False, do_glob = False)
  1612.  
  1613.  
  1614.  
  1615. class Color:
  1616.     normal = '\x1b[0m'
  1617.     black = '\x1b[30m'
  1618.     red = '\x1b[31m'
  1619.     green = '\x1b[32m'
  1620.     yellow = '\x1b[33m'
  1621.     blue = '\x1b[34m'
  1622.     purple = '\x1b[35m'
  1623.     cyan = '\x1b[36m'
  1624.     bold = '\x1b[1m'
  1625.     uline = '\x1b[4m'
  1626.     blink = '\x1b[5m'
  1627.     invert = '\x1b[7m'
  1628.     
  1629.     def Punctuation(s):
  1630.         return Color.normal + s + Color.normal
  1631.  
  1632.     Punctuation = staticmethod(Punctuation)
  1633.     
  1634.     def FieldValue(s):
  1635.         if not isinstance(s, str):
  1636.             s = str(s)
  1637.         
  1638.         return Color.purple + s + Color.normal
  1639.  
  1640.     FieldValue = staticmethod(FieldValue)
  1641.     
  1642.     def FieldName(s):
  1643.         return Color.blue + s + Color.normal
  1644.  
  1645.     FieldName = staticmethod(FieldName)
  1646.     
  1647.     def ClassName(s):
  1648.         return Color.red + Color.bold + s + Color.normal
  1649.  
  1650.     ClassName = staticmethod(ClassName)
  1651.     
  1652.     def Simple(s, color):
  1653.         if not isinstance(s, str):
  1654.             s = str(s)
  1655.         
  1656.         
  1657.         try:
  1658.             color_attr = getattr(Color, color)
  1659.         except AttributeError:
  1660.             return s
  1661.  
  1662.         return color_attr + s + Color.normal
  1663.  
  1664.     Simple = staticmethod(Simple)
  1665.  
  1666.  
  1667. def command_line():
  1668.     OptionParser = OptionParser
  1669.     import optparse
  1670.     usage = 'usage: %prog [options] [path1] [path2] [pathn]'
  1671.     parser = OptionParser(usage = usage)
  1672.     parser.add_option('-v', '--verbose', action = 'store_true', dest = 'verbose', help = 'Verbose mode')
  1673.     parser.add_option('-r', '--recursive', action = 'store_true', dest = 'recursive', help = 'Add watches recursively on paths')
  1674.     parser.add_option('-a', '--auto_add', action = 'store_true', dest = 'auto_add', help = 'Automatically add watches on new directories')
  1675.     parser.add_option('-e', '--events-list', metavar = 'EVENT[,...]', dest = 'events_list', help = 'A comma-separated list of events to watch for - see the documentation for valid options (defaults to everything)')
  1676.     parser.add_option('-s', '--stats', action = 'store_true', dest = 'stats', help = 'Display statistics')
  1677.     (options, args) = parser.parse_args()
  1678.     if options.verbose:
  1679.         log.setLevel(10)
  1680.     
  1681.     if len(args) < 1:
  1682.         path = '/tmp'
  1683.     else:
  1684.         path = args
  1685.     wm = WatchManager()
  1686.     if options.stats:
  1687.         notifier = Notifier(wm, default_proc_fun = Stats(), read_freq = 5)
  1688.     else:
  1689.         notifier = Notifier(wm)
  1690.     mask = 0
  1691.     if options.events_list:
  1692.         events_list = options.events_list.split(',')
  1693.         for ev in events_list:
  1694.             evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
  1695.             if evcode:
  1696.                 mask |= evcode
  1697.                 continue
  1698.             parser.error("The event '%s' specified with option -e is not valid" % ev)
  1699.         
  1700.     else:
  1701.         mask = ALL_EVENTS
  1702.     cb_fun = None
  1703.     if options.stats:
  1704.         
  1705.         def cb(s):
  1706.             print '%s\n%s\n' % (repr(s.proc_fun()), s.proc_fun())
  1707.  
  1708.         cb_fun = cb
  1709.     
  1710.     log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
  1711.     wm.add_watch(path, mask, rec = options.recursive, auto_add = options.auto_add)
  1712.     notifier.loop(callback = cb_fun)
  1713.  
  1714. if __name__ == '__main__':
  1715.     command_line()
  1716.  
  1717.